/*
 * Copyright (c) 2006-2025, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2025-10-14     RT-Thread    ds3231 driver
 */

#include <rtthread.h>
#include <rtdevice.h>
#include "drv_ds3231.h"

#define DBG_TAG "drv.ds3231"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>

static rt_uint8_t bcd_to_dec(rt_uint8_t bcd)
{
    return (bcd >> 4) * 10 + (bcd & 0x0F);
}

static rt_uint8_t dec_to_bcd(rt_uint8_t dec)
{
    return ((dec / 10) << 4) | (dec % 10);
}

static rt_err_t ds3231_write_reg(ds3231_device_t dev, rt_uint8_t reg, rt_uint8_t *data, rt_size_t len)
{
    struct rt_i2c_msg msgs[2];
    
    msgs[0].addr = DS3231_I2C_ADDR;
    msgs[0].flags = RT_I2C_WR;
    msgs[0].buf = &reg;
    msgs[0].len = 1;
    
    msgs[1].addr = DS3231_I2C_ADDR;
    msgs[1].flags = RT_I2C_WR | RT_I2C_NO_START;
    msgs[1].buf = data;
    msgs[1].len = len;

    if (rt_i2c_transfer(dev->i2c, msgs, 2) == 2)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

static rt_err_t ds3231_read_reg(ds3231_device_t dev, rt_uint8_t reg, rt_uint8_t *buf, rt_size_t len)
{
    struct rt_i2c_msg msgs[2];

    msgs[0].addr = DS3231_I2C_ADDR;
    msgs[0].flags = RT_I2C_WR;
    msgs[0].buf = &reg;
    msgs[0].len = 1;

    msgs[1].addr = DS3231_I2C_ADDR;
    msgs[1].flags = RT_I2C_RD;
    msgs[1].buf = buf;
    msgs[1].len = len;

    if (rt_i2c_transfer(dev->i2c, msgs, 2) == 2)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

ds3231_device_t ds3231_init(const char *i2c_bus_name)
{
    ds3231_device_t dev = RT_NULL;
    rt_uint8_t control_reg;
    rt_uint8_t status_reg;

    dev = rt_calloc(1, sizeof(struct ds3231_device));
    if (dev == RT_NULL)
    {
        LOG_E("Can't allocate memory for ds3231 device");
        return RT_NULL;
    }

    dev->i2c = (struct rt_i2c_bus_device *)rt_device_find(i2c_bus_name);
    if (dev->i2c == RT_NULL)
    {
        LOG_E("Can't find %s device", i2c_bus_name);
        rt_free(dev);
        return RT_NULL;
    }

    dev->lock = rt_mutex_create("ds3231", RT_IPC_FLAG_FIFO);
    if (dev->lock == RT_NULL)
    {
        LOG_E("Can't create mutex for ds3231 device");
        rt_free(dev);
        return RT_NULL;
    }

    if (ds3231_read_reg(dev, DS3231_REG_CONTROL, &control_reg, 1) != RT_EOK)
    {
        LOG_E("DS3231 read control register failed");
        rt_mutex_delete(dev->lock);
        rt_free(dev);
        return RT_NULL;
    }

    control_reg &= ~(1 << 7);
    control_reg &= ~(1 << 2);
    
    if (ds3231_write_reg(dev, DS3231_REG_CONTROL, &control_reg, 1) != RT_EOK)
    {
        LOG_E("DS3231 write control register failed");
        rt_mutex_delete(dev->lock);
        rt_free(dev);
        return RT_NULL;
    }

    if (ds3231_read_reg(dev, DS3231_REG_STATUS, &status_reg, 1) != RT_EOK)
    {
        LOG_E("DS3231 read status register failed");
        rt_mutex_delete(dev->lock);
        rt_free(dev);
        return RT_NULL;
    }

    status_reg &= ~(1 << 7);
    
    if (ds3231_write_reg(dev, DS3231_REG_STATUS, &status_reg, 1) != RT_EOK)
    {
        LOG_E("DS3231 write status register failed");
        rt_mutex_delete(dev->lock);
        rt_free(dev);
        return RT_NULL;
    }

    LOG_I("DS3231 init success");

    return dev;
}

void ds3231_deinit(ds3231_device_t dev)
{
    if (dev != RT_NULL)
    {
        rt_mutex_delete(dev->lock);
        rt_free(dev);
    }
}

rt_err_t ds3231_read_time(ds3231_device_t dev, struct ds3231_time *time)
{
    rt_uint8_t buf[7];
    rt_err_t result = RT_EOK;

    if (dev == RT_NULL || time == RT_NULL)
    {
        return -RT_ERROR;
    }

    rt_mutex_take(dev->lock, RT_WAITING_FOREVER);

    result = ds3231_read_reg(dev, DS3231_REG_SECONDS, buf, 7);
    if (result != RT_EOK)
    {
        LOG_E("DS3231 read time failed");
        rt_mutex_release(dev->lock);
        return result;
    }

    time->second = bcd_to_dec(buf[0] & 0x7F);
    time->minute = bcd_to_dec(buf[1] & 0x7F);
    time->hour = bcd_to_dec(buf[2] & 0x3F);
    time->day = bcd_to_dec(buf[3] & 0x07);
    time->date = bcd_to_dec(buf[4] & 0x3F);
    time->month = bcd_to_dec(buf[5] & 0x1F);
    time->year = bcd_to_dec(buf[6]);

    rt_mutex_release(dev->lock);

    return RT_EOK;
}

rt_err_t ds3231_write_time(ds3231_device_t dev, struct ds3231_time *time)
{
    rt_uint8_t buf[7];
    rt_err_t result = RT_EOK;

    if (dev == RT_NULL || time == RT_NULL)
    {
        return -RT_ERROR;
    }

    rt_mutex_take(dev->lock, RT_WAITING_FOREVER);

    buf[0] = dec_to_bcd(time->second);
    buf[1] = dec_to_bcd(time->minute);
    buf[2] = dec_to_bcd(time->hour);
    buf[3] = dec_to_bcd(time->day);
    buf[4] = dec_to_bcd(time->date);
    buf[5] = dec_to_bcd(time->month);
    buf[6] = dec_to_bcd(time->year);

    result = ds3231_write_reg(dev, DS3231_REG_SECONDS, buf, 7);
    if (result != RT_EOK)
    {
        LOG_E("DS3231 write time failed");
        rt_mutex_release(dev->lock);
        return result;
    }

    rt_mutex_release(dev->lock);

    return RT_EOK;
}

rt_err_t ds3231_read_temperature(ds3231_device_t dev, float *temperature)
{
    rt_uint8_t buf[2];
    rt_err_t result = RT_EOK;
    rt_int16_t temp;

    if (dev == RT_NULL || temperature == RT_NULL)
    {
        return -RT_ERROR;
    }

    rt_mutex_take(dev->lock, RT_WAITING_FOREVER);

    result = ds3231_read_reg(dev, DS3231_REG_TEMP_MSB, buf, 2);
    if (result != RT_EOK)
    {
        LOG_E("DS3231 read temperature failed");
        rt_mutex_release(dev->lock);
        return result;
    }

    temp = (rt_int16_t)((buf[0] << 8) | buf[1]) >> 6;
    *temperature = temp * 0.25;

    rt_mutex_release(dev->lock);

    return RT_EOK;
}
